package com.wontube.core.pub;

import java.io.File;
import java.io.IOException;
import java.util.Collections;
import java.util.HashMap;
import java.util.ArrayList;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.wontube.client.conf.ServerEnvironment;
import com.wontube.client.extension.utils.FileUtils;
import com.wontube.core.annotation.HandlerScope;
import com.wontube.core.conf.Database;
import com.wontube.core.conf.Datasource;
import com.wontube.core.dao.SqlSessionManager;
import com.wontube.core.plugin.IPlugin;
import com.wontube.core.plugin.PluginComparator;
import com.wontube.core.pub.servlet.handlers.HandlerWrapper;
import com.wontube.core.remote.IHandler;
import com.wontube.core.utils.ClassUtils;

/**
 * 
 * @author yuechao
 * 
 *         提供对服务器上下文环境（中间层全局变量）的访问
 * 
 */
public final class AppEnvironment {
	private AppEnvironment() {
	}
	
	private static String appPath;
	
	public static void init(String appPath) {
		AppEnvironment.appPath = appPath;
		logger = LoggerFactory.getLogger(AppEnvironment.class);
	}

	/**
	 * 应用程序根目录。在系统初始化时赋值
	 * 
	 */
	public static String getAppPath() {
		return AppEnvironment.appPath;
	}
	
	private static Logger logger;

	/**
	 * 服务器间通讯的agent与数据格式
	 */
	public static final String serverAgent = "__server__";
	
	/**
	 * 文件数据存储路径
	 * 
	 */
	public static String dataPath;
	
	/**
	 * 核心根包名，不要在运行时修改
	 * 
	 */
	public static final String corePackageRoot = "com.wontube.core";

	/**
	 * 根包名，可以根据项目需要在系统启动前修改
	 * 
	 */
	public static String packageRoot = "com.wontube.smr";
	
	/** 接口前缀, 根据项目在启动前修改 */
	public static String clientPackagePrefix = "com.wontube.";
	
	/** 框架接口前缀, 是固定的，不要在运行时修改 */
	public static final String coreClientPackagePrefix = "com.wontube.";
	
	/** mybatis配置文件classpath地址 */
	public static String daoConfigPath;

	/**
	 * 客户数据源配置文件，在启动时修改
	 * 
	 */
	public static String customerConfigPath;

	/**
	 * 服务器classes相对路径, 可根据 Servlet容器配置在启动时更改
	 * 
	 */
	public static String relativeClassesRootPath = FileUtils.combinePath("WEB-INF", "classes");
	
	/**  
	 * 服务器配置文件相对路径, 可根据 Servlet容器配置在启动时更改 
	 * 
	 */
	public static String relativeConfigRootPath = FileUtils.combinePath("WEB-INF", "conf");	

	/**
	 * 整个系统一使用"UTF-8"编码
	 * 
	 */
	public static final String charsetName = ServerEnvironment.charsetName;

	/**
	 * 插件
	 * 
	 */
	public static final ArrayList<IPlugin> pluginList = new ArrayList<IPlugin>();

	/**
	 * Handler 接口类型名到相应的实现类包装之间的映射
	 * 
	 */
	public static final HashMap<String, HandlerWrapper> handlerMap = new HashMap<String, HandlerWrapper>();

	/**
	 * 获取绝对路径
	 * 
	 * @param path
	 *            相对根目录的路径
	 * @return
	 */
	public static String getRealPath(String... args) {
		String path = FileUtils.combinePath(args);
		return FileUtils.combinePath(AppEnvironment.appPath, path);
	}

	public static String getClassesRootPath() {
		return AppEnvironment.getRealPath(AppEnvironment.relativeClassesRootPath);
	}
	
	public static String processInterfaceName(String clazzName) {
		if (clazzName.startsWith(clientPackagePrefix)) {
			clazzName = clazzName.substring(clientPackagePrefix.length());
		} else if (clazzName.startsWith(coreClientPackagePrefix)) {
			clazzName = clazzName.substring(coreClientPackagePrefix.length());
		}
		
		return clazzName;
	}

	/**
	 * load static datasource
	 * 
	 */
	public static boolean initializeStaticDatasource() {
		logger.info("begins loading static datasource");
		String path = AppEnvironment.getRealPath(AppEnvironment.relativeConfigRootPath, "datasource.json");
		Datasource datasource = Datasource.load(new File(path));

		if (datasource.getDatabases().size() < 1) {
			logger.warn("failed to load {}/datasource.json!", AppEnvironment.relativeConfigRootPath);
			return false;
		}

		// 替换环境变量
		if (datasource.getDataPath() != null) {
			dataPath = datasource.getDataPath().replace("{APP|PATH}", AppEnvironment.appPath);
		}

		for (Database db : datasource.getDatabases()) {
			String url = db.getUrl().replace("{APP|PATH}", AppEnvironment.appPath);
			db.setUrl(url);
		}

		try {
			SqlSessionManager.manager.initializeStaticDatasource(datasource);
		} catch (IOException e) {
			e.printStackTrace();
			logger.warn("failed to initinalize static datasource");
			return false;
		}

		logger.info("finished loading static datasource");
		return true;
	}

	/*
	 * load customer datasource, it should be called
	 * 
	 * after load the static datasource
	 */
	public static boolean initCustomerDatasource(List<CustomerDatasource> list) {
		logger.info("begins loading customer datasource");

		try {
			SqlSessionManager.manager.initDynamicDatasource(list);
		} catch (Exception e) {
			e.printStackTrace();
			logger.info("failed loading customer datasource");
			return false;
		}

		logger.info("finished loading customer datasource");

		return true;
	}

	/**
	 * 加载插件
	 */
	public static final void loadHandlerPlugin() {
		logger.info("begins loading request handler chain plugin");

		List<IPlugin> list = AppEnvironment.pluginList;
		list.clear();

		Class<IPlugin> iPluginClass = IPlugin.class;
		for (Class<IPlugin> pluginClass : ClassUtils.loadSubClasses(iPluginClass)) {
			try {
				list.add(pluginClass.newInstance());

			} catch (Exception e) {
				logger.warn(e.toString());
			}
		}

		// 插件排序
		Collections.sort(AppEnvironment.pluginList, new PluginComparator());

		if (logger.isInfoEnabled()) {
			for (IPlugin obj : AppEnvironment.pluginList) {
				logger.info("plugin install [{}]", obj.getClass().getName());
			}

			logger.info("finish loading request chain plugin");
		}
	}

	/**
	 * 加载处理器
	 * 
	 */
	public static final void loadHandler() {
		logger.info("begins loading handler classes");

		HashMap<String, HandlerWrapper> map = AppEnvironment.handlerMap;
		map.clear();

		Class<IHandler> handlerClazz = IHandler.class;
		for (Class<IHandler> cls : ClassUtils.loadSubClasses(handlerClazz)) {
			try {
				Scope scope = Scope.Singleton;
				HandlerScope handlerScope = cls.getAnnotation(HandlerScope.class);
				if (handlerScope != null) {
					scope = handlerScope.scope();
				}

				HandlerWrapper wrapper = new HandlerWrapper(cls, scope);

				// 本身类名到实例的映射, 用于远程手工调用
				//map.put(cls.getName(), wrapper);
				logger.debug("handler: {}", cls.getName());

				for (Class<?> remoteClazz : cls.getInterfaces()) {
					if (handlerClazz.equals(remoteClazz)) {
						continue;
					}
					
					// 处理包前缀
					String clazzName = remoteClazz.getName();
					clazzName = processInterfaceName(clazzName);

					if (map.containsKey(clazzName)) {
						logger.error("duplicate class {} for handler, last handler: {}",
								clazzName, map.get(cls.getName()).getClass().getName());
					} else {
						// 接口与实现的映射, 用于远程代理调用
						map.put(clazzName, wrapper);
						logger.debug("handler (scope: {}): {} -> {}", new String[] { scope.toString(), clazzName, cls.getName() });
					}
				}
			} catch (Exception e) {
				logger.error(e.toString());
			}
		}

		logger.info("finish loading handler classes");
	}
}
